package com.zaicheng.cloud;

import com.zaicheng.cloud.annotation.RemoteCallClient;
import com.zaicheng.cloud.processor.ClassPathScanningCandidateComponent;
import com.zaicheng.cloud.remotecallclient.RemoteCallClientFactoryBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.*;

/**
 @author zaicheng
 @version 1.0
 @date 2022-04-19
 @email zzc15847256473@163.com
 */
@Slf4j
public class RemoteCallsRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private Environment environment;

    public RemoteCallsRegistrar() {
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {

        //存放候选组件的集合
        LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();

        // 获取扫描器
        ClassPathScanningCandidateComponent scanner = new ClassPathScanningCandidateComponent(beanDefinitionRegistry);

        scanner.addIncludeFilter(new AnnotationTypeFilter(RemoteCallClient.class));

        // 获取注解上配置的包或者默认包
        Set<String> basePackages = this.getBasePackages(annotationMetadata);


        Iterator<String> iterators = basePackages.iterator();

        // 遍历包，找到候选的组件
        while (iterators.hasNext()){
            String packageName = iterators.next();
//            System.out.println(packageName);
            // 获取所有的候选组件
            try {
                candidateComponents.addAll(scanner.findCandidateComponents(packageName));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        // 找到所有的候选bean定义
        Iterator<BeanDefinition> definitionIterator = candidateComponents.iterator();

        while (definitionIterator.hasNext()){

            BeanDefinition componentCandidate = definitionIterator.next();

//            System.out.println(componentCandidate);

            if (componentCandidate instanceof AnnotatedBeanDefinition){

                AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) componentCandidate;

                //获取注解元数据
                AnnotationMetadata metadata = beanDefinition.getMetadata();

                Assert.isTrue(metadata.isInterface(),"@RemoteCallClient 仅仅只能指定在接口上");

                Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(RemoteCallClient.class.getCanonicalName());

//                log.error("@RemoteCallClient属性："+annotationAttributes);

                //注册RemoteCallclient
                this.registerRemoteCallClient(beanDefinitionRegistry,metadata,annotationAttributes);


            }

        }


    }

    // 将我们的bean定义对象注册到Sprnig容器，生成的对象是我们RemoteCallClientFactoryBean的getObject方法获取到的代理对象
    private void registerRemoteCallClient(BeanDefinitionRegistry registry, AnnotationMetadata metadata, Map<String, Object> annotationAttributes) {

        String className = metadata.getClassName();
        log.info("className："+className);
        //构建bean定义
        BeanDefinitionBuilder beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(RemoteCallClientFactoryBean.class);

        // 获取我们的服务名字
        String name = this.getName(annotationAttributes);

        // 给我们的bean定义的属性赋值：服务名
        beanDefinition.addPropertyValue("name",name);

        // 服务名
        beanDefinition.addPropertyValue("value",name);

        // 请求的url
        beanDefinition.addPropertyValue("url",this.getUrl(annotationAttributes));

        String conetxtId = this.getContextId(annotationAttributes);
        // 上下文id
        beanDefinition.addPropertyValue("contextId",conetxtId);
        beanDefinition.addPropertyValue("type", className);
        beanDefinition.addPropertyValue("path",this.getPath(annotationAttributes));

        // 降级
        //beanDefinition.addPropertyValue("fallBack",null);

        // 降级工厂
        //beanDefinition.addPropertyValue("fallBackFactory",null);

        beanDefinition.setAutowireMode(2);
        String alias = conetxtId + "RemoteCallClient";
        AbstractBeanDefinition definition = beanDefinition.getBeanDefinition();
        definition.setAttribute("factoryBeanObjectType", className);

        String qualifier = this.getQualifier(annotationAttributes);
        if (StringUtils.hasText(qualifier)) {
            alias = qualifier;
        }
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(definition,className,new String[]{alias});

        // 注册到Spring容器
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder,registry);

    }

    private String getQualifier(Map<String, Object> client) {
        if (client == null) {
            return null;
        } else {
            String qualifier = (String)client.get("qualifier");
            return StringUtils.hasText(qualifier) ? qualifier : null;
        }
    }

    private String resolve(String name) {
        return StringUtils.hasText(name) ? this.environment.resolvePlaceholders(name) : name;
    }

    // 从注解属性获取包名或者从主启动类上获取包名
    public Set<String>  getBasePackages(AnnotationMetadata annotationMetadata){

        Set<String> basePackages = new HashSet();

        Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(EnableRemoteCallClients.class.getCanonicalName());

        String[] value4 = (String[])((String[])attributes.get("value"));

        String pkg;

        for(int value5 = 0; value5 < value4.length; ++value5) {
            pkg = value4[value5];
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }

        value4 = (String[])((String[])attributes.get("basePackages"));


        for(int value5 = 0; value5 < value4.length; ++value5) {
            pkg = value4[value5];
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }

        Class[] packageClasses = (Class[])((Class[])attributes.get("basePackageClasses"));

        for(int value5 = 0; value5 < packageClasses.length; ++value5) {
            Class<?> clazz = packageClasses[value5];
            basePackages.add(ClassUtils.getPackageName(clazz));
        }

        if (basePackages.isEmpty()){
            String packageName = ClassUtils.getPackageName(annotationMetadata.getClassName());
            basePackages.add(packageName);
        }

        return basePackages;
    }


    // 获取url
    private String getUrl(Map<String, Object> annotationAttributes) {
        String url = this.resolve((String) annotationAttributes.get("url"));
        return  getUrl(url);
    }

    static String getUrl(String url){
        if (StringUtils.hasText(url) && (!url.startsWith("#{") || !url.contains("}"))){
            if (!url.contains("://")){
                url = "http://" + url;
            }
            try {
                new URL(url);
            }catch (MalformedURLException e) {
                throw  new IllegalArgumentException(url + "is MalformedURL" + e);
            }
        }
        return url;
    }


    // 服务名
    private String getName(Map<String, Object> annotationAttributes) {

        String name = (String) annotationAttributes.get("serviceId");

        if (!StringUtils.hasText(name)){
            name = (String) annotationAttributes.get("name");
        }

        if (!StringUtils.hasText(name)){
            name = (String) annotationAttributes.get("value");
        }
        name = this.resolve(name);
        return  getName(name);
    }

    static String getName(String name){
        if (!StringUtils.hasText(name)){
            return  "";
        }else {
            String host = null;
            try {
                String url = null;
                if (!name.startsWith("http://") && !name.startsWith("https://")){
                    url = "http://" + name;
                }else {
                    url = name;
                }
                host = (new URI(url)).getHost();

            }catch (Exception e){
                e.printStackTrace();
            }
            Assert.state(host!=null,"服务不是一个合法主机名："+name);
        }
        return  name;
    }

    // 获取path
    private String getPath(Map<String, Object> annotationAttributes) {
        String path = this.resolve((String)annotationAttributes.get("path"));
        return  getPath(path);
    }

    static  String getPath(String path){
        if (StringUtils.hasText(path)){
            path = path.trim();
            if (!path.startsWith("/")){
                path = "/" + path;
            }

            if (path.endsWith("/")){
                path = path.substring(0,path.length()-1);
            }

        }
        return path;
    }

    // 上下文id
    private String getContextId(Map<String, Object> annotationAttributes) {
        String contextId = (String) annotationAttributes.get("contextId");
        if (!StringUtils.hasText(contextId)){
            return  this.getName(annotationAttributes);
        }else {
            contextId = this.resolve(contextId);
            return  getName(contextId);
        }
    }

    // 降级

    // 降级工厂


    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

}
